//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <winhack.h>
_ELASTOS_NAMESPACE_USING

#include <CMalloc.h>
#include <stdlib.h>
#include <stdio.h>
#include <sysconfig.h>

// For memory boundary checking
#if defined(_GNUC)
#define MEMALLOC_PRE_MAGIC   0x80868088L
#define MEMALLOC_POST_MAGIC  0x8086808880868088LL
#else
#define MEMALLOC_PRE_MAGIC   0x80868088
#define MEMALLOC_POST_MAGIC  0x8086808880868088
#endif

#define TIMEOUT 1000
#define MAX_ENVVAR 512

#define MAGIC2BYTES 8

unsigned int g_HeapDebugSum = (unsigned int)-1;
unsigned int g_HeapDebugObj = (unsigned int)-1;
unsigned int g_HeapDebugSize = 0;
unsigned int g_HeapDebugIdx = 0;

CMalloc* g_pMalloc = NULL;
wchar_t  g_traceFlag[20] = {0};
extern Int32 g_totalMemSpySize;

#ifdef _MSVC

//EXTERN_C Address GetThreadStackTop(IThread *pThd);

static Address GetCurrentStackTop()
{
    unsigned long stktop;
#ifdef _MSVC
    __asm mov stktop, ebp;
#else
    __asm__ volatile (
        "mov %%ebp, %0;"
        : "=a" (stktop));
#endif
    stktop = (stktop + 0x1000 - 1) & ~(0x1000 - 1);//Roundup
    return stktop;
}

//extern BOOL g_bTrackStack;
EXTERN_C void GetCallerStack(Address *pCallstk, int level, Address limit);
EXTERN_C BOOL DumpStack(Address *pCallstk, int level);

extern void SetRefCheck(PInterface pObj, unsigned long sum);
extern void DumpObjRefStack();

EXTERN_C wchar_t * CDECL _GetEnvironment(const wchar_t *envstring);
extern "C" int __cdecl swscanf( const wchar_t *buffer, const wchar_t *format, ... );
EXTERN_C Address __stdcall _RefHook(Address pObj, int n);

void OnRefObject(int eType, int nCode, void *pParam)
{
    _RefHook((Address)pParam, nCode);
}

void CallObjRefDump(int eType, int nCode, void *pParam)
{
    DumpObjRefStack();
}

void OnCreateObject(int eType, int nCode, void *pParam)
{
    PInterface pObj = (PInterface)pParam;
    if ((Address)pObj - g_HeapDebugObj > 8) return ;
    //_SysUnhook(eType);

    SetRefCheck((PInterface)g_HeapDebugObj, g_HeapDebugSum);
    g_HeapDebugObj = g_HeapDebugSum = -1;//Close
}

#endif

extern "C" PVoid  Aura_malloc(Int32 size);
extern "C" PVoid  Aura_realloc(PVoid  p,Int32 size);
extern "C" void   Aura_free(PVoid p);
extern "C" int __cdecl Aura_printf(const char * fmt, ...);

extern "C" int __cdecl _wtoi(const wchar_t * string);

unsigned int GetWindowSettingHex(wchar_t *szItem)
{
    unsigned int retValue = 0;
    wchar_t  szContent[MAX_ENVVAR];

    retValue = GetEnvironmentVariableW(szItem, szContent, MAX_ENVVAR);
    if (retValue == 0) {
		    return 0xffffffff;
    }

    retValue = _wtoi(szContent);

    return retValue;
}


// multiple of 8 bytes
typedef struct _MSPYNODE MSPYNODE, *PMSPYNODE;
struct _MSPYNODE
{
    DoubleLinkNode   m_MSpyNode;        // Hashed by m_AllocBlock (Inst)
    void*            m_allocBlock;       // Alloc Block
    ULONG            m_allocSize;        // Alloc Size for user
    ULONG            m_allocIndex;       // the allocation pass count.
    unsigned int     m_threadId;         // thread id
    ULONG            m_threadIndex;      // alloc index in a thread
    // the highest bit is used by
    //   DumpInstCompact, so the max index
    //   can't exceed 2^32 - 2
    TIME        m_timestamp;            // alloc time
#define MAX_STACK_LEVEL 20
    Address     m_callstk[MAX_STACK_LEVEL];           // calling stack. It will alloc
    //   more spaces immediately after this
    //   struct for the stack. So m_callstk
    //   is actually an array.
    ULONG       m_stkchksum;            //  MAX LEVEL + 1
};

/* Heap memory block headers, 16 bytes each
 */
#define USEDSIGN "USED"
#define FREESIGN "FREE"
typedef struct _USEDBLK USEDBLK, *PUSEDBLK;
struct _USEDBLK
{ /* Allocated heap memory (multiple of 8 bytes)  */
    ULONG Sign;    /* Signature                                    */
    ULONG Size;    /* Includes header.                             */
    PMSPYNODE pMSpyNode;
    ULONG CheckMark;
};

EXTERN_C TIME _GetTickCount()
{
    LARGE_INTEGER freq, count;
    BOOL bRes = FALSE;

    bRes = QueryPerformanceFrequency(&freq);
    assert(bRes);
    bRes = QueryPerformanceCounter(&count);
    assert(bRes);

    double Ticks = count.QuadPart * (1000000.0 / freq.QuadPart);
    return (TIME)Ticks;//UNIT: uS
}

INLINE void SetUsedBlk(PUSEDBLK pUsedBlk, size_t Size)
{
    pUsedBlk->Sign = *(UINT *)USEDSIGN;
    pUsedBlk->Size = Size;
    pUsedBlk->pMSpyNode = NULL;
    pUsedBlk->CheckMark = MEMALLOC_PRE_MAGIC;
}

INLINE void SetFreeBlk(PUSEDBLK pUsedBlk)
{
    pUsedBlk->Sign = *(UINT *)FREESIGN;
    pUsedBlk->pMSpyNode = NULL;
}

INLINE size_t  GetUsedBlkSize(PUSEDBLK pUsedBlk)
{
    assert(pUsedBlk->Sign == *(UINT *)USEDSIGN && "Heap Block Sign Error!");

    return pUsedBlk->Size;
}

INLINE size_t  GetUserAllocSize(PUSEDBLK pUserBlk)
{
    assert(pUserBlk->Sign == *(UINT *)USEDSIGN && "Heap Block Sign Error!");
    assert(pUserBlk->Size >= (sizeof(USEDBLK) + MAGIC2BYTES));

    return pUserBlk->Size - (sizeof(USEDBLK) - MAGIC2BYTES);
}

INLINE void SetHeapBlockContent(void *pMem, size_t Size)
{
    memset(pMem, 0xDD, Size);
}

INLINE void ClearHeapBlockContent(void *pMem, size_t Size)
{
    memset(pMem, 0xCC, Size);
}

PVoid CMalloc::Alloc(size_t Size)
{
    PUSEDBLK p;
    size_t TotalSize;
    //LNOTENOTE: Why no need roundup Size?
    TotalSize = Size + sizeof(USEDBLK) + MAGIC2BYTES;

    p = (PUSEDBLK)Aura_malloc(TotalSize);

    if (p != NULL) {
        SetUsedBlk(p, TotalSize);
        p++;
        *(UINT64 *)((byte *)p + Size) = MEMALLOC_POST_MAGIC;
        SetHeapBlockContent(p, Size);
    }

    return (PVoid)p;
}

PVoid CMalloc::Realloc(PVoid pMem, size_t NewSize)
{
    PUSEDBLK p = (PUSEDBLK)pMem;
    size_t TotalSize;

    if (pMem == NULL) {
        // Allocate a new buffer
        return CMalloc::Alloc(NewSize);
    }
    else if (pMem && NewSize == 0) {
        CMalloc::Free(pMem);
        return NULL;
    }

    TotalSize = NewSize + sizeof(USEDBLK) + MAGIC2BYTES;

    p--;
    p = (PUSEDBLK)Aura_realloc(p, TotalSize);
    if (NULL != p) {
        SetUsedBlk(p, TotalSize);
        p++;
        *(UINT64 *)((byte *)p + NewSize) = MEMALLOC_POST_MAGIC;
    }

    return (PVoid)p;
}

void CMalloc::Free(PVoid pMem)
{
    if (pMem == NULL)
        return;

    PUSEDBLK p = (PUSEDBLK)pMem;
    p--;
    ClearHeapBlockContent(pMem, p->Size - sizeof(USEDBLK) - MAGIC2BYTES);
    SetFreeBlk(p); // Perhaps it is useful for debugging
    Aura_free(p);
}

size_t CMalloc::GetSize(PVoid pMem)
{
    PUSEDBLK p = (PUSEDBLK)pMem - 1;
    return GetUsedBlkSize(p) - sizeof(USEDBLK) - MAGIC2BYTES;
}

CMSpyMalloc::CMSpyMalloc()
{
    m_pMSpyNodeList = NULL;
    m_allocIdx = 0;
    m_nTotalHeapBlocks = 0;
    m_nTotalHeapSize = 0;
    m_nMaxHeapBlocks = 0;
    m_nMaxHeapBlockSize = 0;
    m_nMaxHeapSize = 0;
    m_nheapLimitSize = 0x4000000/*64M*/;
    InitializeCriticalSection(&m_Lock);
    //ConfigGetItemInt(L"HeapLimitSize", (int *)&m_nheapLimitSize);BUGBUG:There is no configDic in same process
}

CMSpyMalloc::~CMSpyMalloc()
{
    DeleteCriticalSection(&m_Lock);
}

// Some additional operation before memory alloc.
void CMSpyMalloc::PreAlloc(size_t Size)
{
    m_allocIdx++; // Add it either success or fail

    if (m_allocIdx == g_HeapDebugIdx) {
        Aura_printf("== Now Allocating the heap memory block"\
                "you want to debug (index = %d)! ==\n",
                m_allocIdx);
        DebugBreak();
    }

    if (Size == g_HeapDebugSize) {
        // memtrace by size
        Aura_printf("== Now Allocating the heap memory block"\
                "you want to debug (Size = %d)! ==\n",
                Size);
        DebugBreak();
    }
/*
//    if (Size == 0) {
//        wprintf(L"WARNING: The allocating size is 0. "\
//                L"Actually the allocating size will be %d bytes\n", HB_MINBLOCKSIZE);
//    }
*/
    return;
}

// Some additional operation after memory alloc(realloc).
void CMSpyMalloc::PostAlloc(PVoid pMem, size_t UserSize)
{
    PMSPYNODE pMSpyNode;
    PUSEDBLK pUsedBlk = (PUSEDBLK)pMem - 1;

    // Allocating memory for spy node
    pMSpyNode = (PMSPYNODE)Aura_malloc(sizeof(MSPYNODE));
    assert(pMSpyNode);
    pUsedBlk->pMSpyNode = pMSpyNode;

    if (m_pMSpyNodeList == NULL) {
        m_pMSpyNodeList = &pMSpyNode->m_MSpyNode;
        m_pMSpyNodeList->Initialize();
    }

    pMSpyNode->m_allocIndex = m_allocIdx;
    pMSpyNode->m_threadId = GetCurrentThreadId();
    m_pMSpyNodeList->m_pPrev->InsertNext(&(pMSpyNode->m_MSpyNode));
    pMSpyNode->m_allocBlock = pMem;
    pMSpyNode->m_allocSize = UserSize;
    pMSpyNode->m_timestamp = _GetTickCount();

#ifdef _MSVC
    //IThread *pThd;
    //_CThread_GetCurrent(&pThd);
    Address stacktop = GetCurrentStackTop();//GetThreadStackTop(pThd);
    GetCallerStack(pMSpyNode->m_callstk, MAX_STACK_LEVEL, stacktop);
    
    if (-1 != g_HeapDebugObj || -1 != g_HeapDebugSum) {
        if (g_HeapDebugObj == pMSpyNode->m_stkchksum) {
            Aura_printf("== Now Allocating the heap memory block, "\
                    "you want to debug (obj = 0x%08x, summ = 0x%08x)! ==\n",
                    pMSpyNode->m_allocBlock, pMSpyNode->m_stkchksum);
            g_HeapDebugObj = (Address)pMSpyNode->m_allocBlock;//obj addr
            //_SysSetHook(HOOK_CREATEOBJECT, (void *)OnCreateObject);
            //_SysSetHook(26, (void *)CallObjRefDump);
            //_SysSetHook(27, (void *)OnRefObject);
        }
        else if (g_HeapDebugSum == (g_HeapDebugSum & pMSpyNode->m_stkchksum)) {
            Aura_printf("== Now Allocating the heap memory block, "\
                    "you want to debug (summ = 0x%08x)! ==\n",
                    pMSpyNode->m_stkchksum);
            DebugBreak();
            g_HeapDebugSum = -1;
        }
    }
#endif
    // Here, heap block size doesn't include MSPY_ADDITIONAL_SIZE
    m_nTotalHeapSize += GetUserAllocSize(pUsedBlk);
    if (UserSize > m_nMaxHeapBlockSize) m_nMaxHeapBlockSize = UserSize;
    if (m_nTotalHeapBlocks > m_nMaxHeapBlocks) m_nMaxHeapBlocks = m_nTotalHeapBlocks;
    if (m_nTotalHeapSize > m_nMaxHeapSize) m_nMaxHeapSize = m_nTotalHeapSize;

    return;
}

// Some additional operation before memory free.
void CMSpyMalloc::PreFree(PVoid pMem)
{
    PUSEDBLK pUsedBlk = (PUSEDBLK)pMem - 1;
    PMSPYNODE pMSpyNode = pUsedBlk->pMSpyNode;
    assert(pMSpyNode);

    // Memory boundary checking
    int flag = 0; // 0x1 -- overwrite data after buffer
    // 0x2 -- overwrite data before buffer

    if (*(UINT64 *)((byte *)pUsedBlk + GetUsedBlkSize(pUsedBlk) - MAGIC2BYTES)
        != MEMALLOC_POST_MAGIC) {
        flag = 0x1;
    }

    if (pUsedBlk->CheckMark != MEMALLOC_PRE_MAGIC) flag |= 0x2;

    if (flag) {
        wchar_t *pwszAnnotate;
        if (flag == 0x3)
            pwszAnnotate = L"Before & After";
        else if (flag & 0x1)
            pwszAnnotate = L"     After    ";
        else
            pwszAnnotate = L"    Before    ";
        Aura_printf(
        "\t ****************************************************************\n"\
        "\t **      Detected A Error: Out Of Memory Boundary!             **\n"\
        "\t **      Overwrite Data %s the buffer              **\n"\
        "\t **      Allocated Index : %8d                            **\n" \
        "\t **      Memory Address : 0x%08x,  Size : %8d         **\n"\
        "\t ****************************************************************\n",
        pwszAnnotate, pMSpyNode->m_allocIndex,
        pMSpyNode->m_allocBlock, pMSpyNode->m_allocSize
        );
        // It's a serious problem when memory is out of boundary!
        // So should debug program here!
        assert(0 && "Overflow of memory");
        //debug when overflow
        DebugBreak();
    }

    if (&(pMSpyNode->m_MSpyNode) == m_pMSpyNodeList) {
        m_pMSpyNodeList = pMSpyNode->m_MSpyNode.Next();
        if (m_pMSpyNodeList == &(pMSpyNode->m_MSpyNode)) {
            m_pMSpyNodeList = NULL;
        }
    }

    pMSpyNode->m_MSpyNode.Detach();
    Aura_free(pMSpyNode);
    m_nTotalHeapSize -= GetUserAllocSize(pUsedBlk);
    return ;
}

PVoid CMSpyMalloc::Alloc(size_t Size)
{
    EnterCriticalSection(&m_Lock);
    
    PreAlloc(Size);
    PVoid p = CMalloc::Alloc(Size);
    if (NULL != p) {
        PostAlloc(p, Size);
        m_nTotalHeapBlocks++;
    }
    
    LeaveCriticalSection(&m_Lock);
    return p;
}

PVoid CMSpyMalloc::Realloc(PVoid pMem, size_t NewSize)
{
    PUSEDBLK pUsedBlk;
    size_t OldSize = 0;
    PMSPYNODE pOldMSpyNode = NULL;

    if (pMem == NULL) {
        // Allocate a new buffer
        return CMSpyMalloc::Alloc(NewSize);
    }
    else if (pMem && NewSize == 0) {
        CMSpyMalloc::Free(pMem);
        return NULL;
    }

    EnterCriticalSection(&m_Lock);
    PreAlloc(NewSize);
    
    pUsedBlk = (PUSEDBLK)pMem - 1;
    OldSize = GetUserAllocSize(pUsedBlk);
    pOldMSpyNode = pUsedBlk->pMSpyNode;
    assert(pOldMSpyNode);

    PVoid p = CMalloc::Realloc(pMem, NewSize);
    if (NULL == p) goto exit;

    if (&(pOldMSpyNode->m_MSpyNode) == m_pMSpyNodeList) {
        m_pMSpyNodeList = pOldMSpyNode->m_MSpyNode.m_pNext;
        if (m_pMSpyNodeList == &(pOldMSpyNode->m_MSpyNode)) {
            m_pMSpyNodeList = NULL;
        }
    }
    pOldMSpyNode->m_MSpyNode.Detach();
    Aura_free(pOldMSpyNode);
    
    m_nTotalHeapSize -= OldSize;
    PostAlloc(p, NewSize);
        
exit:    
    LeaveCriticalSection(&(((CMSpyMalloc*)g_pMalloc)->m_Lock));
    return p;
}

void CMSpyMalloc::Free(PVoid pMem)
{
    if (pMem == NULL)
        return;

    EnterCriticalSection(&m_Lock);
    PreFree(pMem);
    CMalloc::Free(pMem);
    m_nTotalHeapBlocks--;
    LeaveCriticalSection(&m_Lock);
}
#ifdef _MSVC

void CMSpyMalloc::TrackStack(ULONG index)
{
    DoubleLinkNode *pMSpyNode = m_pMSpyNodeList;
    if (!pMSpyNode) return;

    ULONG addr = 0;
    if (index & 0x80000000) {
        addr = index & ~0x80000000;
        Aura_printf("============TraceStack addr 0x%x============\n", addr);
    }
    else {
        Aura_printf("============TraceStack index %d============\n", index);
    }

    do {
        MSPYNODE *pSpy = (MSPYNODE *)pMSpyNode;
        if ((0 == addr && pSpy->m_allocIndex == index)
            || (addr - (ULONG)pSpy->m_allocBlock <= 16)) {
            Aura_printf("index=%d, ad=%08x, sz=%u, thd=%d, stamp=%I64x, chksum=%ul\n",
                    pSpy->m_allocIndex, pSpy->m_allocBlock, pSpy->m_allocSize,
                    pSpy->m_threadId, pSpy->m_timestamp, pSpy->m_stkchksum);
            DumpStack(pSpy->m_callstk, MAX_STACK_LEVEL);
            Aura_printf("\n");
        }
    } while ((pMSpyNode = pMSpyNode->Next()) != m_pMSpyNodeList);

    return;
}

EXTERN_C void StackTrack(int index)
{
    ((CMSpyMalloc*)g_pMalloc)->TrackStack((ULONG)index);
}
#endif

void CMSpyMalloc::DumpSpyList(TIME TFlagTime)
{
    DoubleLinkNode *pMSpyNode = m_pMSpyNodeList;
    if (!pMSpyNode)
        return;

    if (TFlagTime == 0) {
        Aura_printf("\n**************** Memory %s Detected ****************\n",
            (TFlagTime)?L"Check":L"Leak");
    }
    
    int counter = 0;
    do {
        MSPYNODE *pSpy = (MSPYNODE *)pMSpyNode;
        if (pSpy->m_timestamp < TFlagTime) continue;

        Aura_printf("index=%d, ad=%08x, sz=%u, thd=%d, stamp=%I64x, chksum=%ul\n",
            pSpy->m_allocIndex, pSpy->m_allocBlock, pSpy->m_allocSize,
            pSpy->m_threadId, pSpy->m_timestamp, pSpy->m_stkchksum);
#ifdef _MSVC
        //        if(g_bTrackStack) {
//            DumpStack(pSpy->m_callstk, MAX_STACK_LEVEL);
        //        }
#endif
        Aura_printf("\n");

        counter++;
    } while ((pMSpyNode = pMSpyNode->Next()) != m_pMSpyNodeList);

    if (counter == 0) {
        Aura_printf("There is no heap memory leaks\n");
        return;//nothing leaks
    }

    Aura_printf("WARNING: Total memory leaks: %d\n\n", counter);
#ifdef _MSVC
    DumpObjRefStack();
#endif
    MemoryStatus();

    Aura_printf("******************************************************\n");
    Aura_printf("Use \"set HEAPDEBUGINDEX=n\" to debug the leak with index n\n");
    Aura_printf("Use \"set HEAPDEBUGSIZE=n\" to debug the leak with allocated size n\n");
#if _MSVC
    Aura_printf("Use \"set HEAPDEBUGSUM=n\" to show the leak with stack checksum\n");
    Aura_printf("Use \"set HEAPDEBUGOBJ=n\" to show the leak with special object\n");
    Aura_printf("Use \"set HEAPDEBUGSTACK=1\" to show the leak with func stack\n");
#endif
    Aura_printf("******************************************************\n");

    return;
}

void CMSpyMalloc::MemoryStatus()
{
    Aura_printf("****************Heap Memory Statistic*****************\n");
    Aura_printf("Used Heap Blocks:        %10d\n", m_nTotalHeapBlocks);
    Aura_printf("Used Heap Bytes:         %10d\n", m_nTotalHeapSize);
    Aura_printf("Maximal Heap Blocks:     %10d\n", m_nMaxHeapBlocks);
    Aura_printf("Maximal Heap Block Size: %10d\n", m_nMaxHeapBlockSize);
    Aura_printf("Maximal Heap Bytes:      %10d\n", m_nMaxHeapSize);
    //Aura_printf("Limit Heap Bytes:        %10d\n", g_cfgVM.LimHeapSize);
    Aura_printf("******************************************************\n");
    Aura_printf("Current AllocIndex:      %10d\n", m_allocIdx);
    Aura_printf("Current TimeStamp:       %I64x\n", _GetTickCount());
}

EXTERN_C void HeapMemoryStatus()
{
    ((CMSpyMalloc*)g_pMalloc)->MemoryStatus();
}

EXTERN_C void CheckHeapMemory(TIME TFlagTime)
{    
    if (0 == wcscmp(g_traceFlag, L"on"))
        ((CMSpyMalloc*)g_pMalloc)->DumpSpyList(TFlagTime);
}

EXTERN_C void DestroyMallocObject()
{    
    if (NULL != g_pMalloc) {
        delete(g_pMalloc);
        g_pMalloc = NULL;
    }
}    

//BUGBUG:only be uesed in pSpyMalloc
EXTERN_C int GetMemState(char *pBuffer)
{
#if 0 //jiuan
    CMSpyMalloc *pSpyMalloc = (CMSpyMalloc *)g_pMalloc;
    unsigned int TotalMemSize = 0;
    IProcess *pProcess;
    ECode ec = NULL;
    ec = _CProcess_GetCurrent(&pProcess);
    if(FAILED(ec)) return 1;
    IObjectEnumerator *pModules;
    ec = pProcess->GetAllModules(&pModules);
    pProcess->Release();
    if(FAILED(ec)) return 1;
    pModules->Reset();
    IModule *pModule;
    Boolean pSucceeded;
    pModules->MoveNext(&pSucceeded);
    for (; ec != S_FALSE; (ec = pModules->MoveNext(&pSucceeded))) {
        pModules->Current((IInterface **)&pModule);
        Int32 size = 0;
//        DECL_WSTRINGBUF(esbName, _MAX_PATH);
//        DECL_WSTRINGBUF(esbPath, _MAX_PATH);
        pModule->GetMemorySize(&size);
        TotalMemSize += size;
    }
    TotalMemSize += pSpyMalloc->m_nTotalHeapSize;
    unsigned int uFreeBytes = pSpyMalloc->m_nheapLimitSize - TotalMemSize;
    PAL_sprintf(pBuffer, "\x02OSLMEM%08x %08x %08x %08x", \
        pSpyMalloc->m_nheapLimitSize, uFreeBytes, TotalMemSize, pSpyMalloc->m_nheapLimitSize | uFreeBytes);
    pModules->Release();

#endif
    return 0;
}

static int s_iState;
Int32 g_maxSpySize = 32;
Int32 g_minSpySize = 0;
Int32 g_valveSpySize = 0;

EXTERN_C int GetHeapState()
{
    return s_iState;
}

#if 0
EXTERN_C BOOL HeapSpySwitch(int iState)
{

    static CMalloc * s_Malloc;
    static CMalloc * s_SpyMalloc;
    static CMalloc * s_PreBChkMalloc;
    static CMalloc * s_PostBChkMalloc;
    static CMalloc * s_pHhyperMalloc;

    switch (iState) {
        case MALLOC: //malloc
            if (NULL == s_Malloc) {
                s_Malloc = new CMalloc();
                if (NULL == s_Malloc) {
                    goto ERR;
                }
            }
            g_pMalloc = s_Malloc;
            break;
        case SPYMALLOC: // spy malloc
            if (NULL == s_SpyMalloc) {
                s_SpyMalloc = (CMalloc*)new CMSpyMalloc();
                if (NULL == s_SpyMalloc) {
                    goto ERR;
                }
            }
            g_pMalloc = s_SpyMalloc;
            break;
        case PREBCHKMALLOC: // pre runtime
            if (NULL == s_PreBChkMalloc) {
                s_PreBChkMalloc = (CMalloc*)new CPreBChkMalloc();
                if (NULL == s_PreBChkMalloc) {
                    goto ERR;
                }
                s_pHhyperMalloc = new CHyperMalloc(s_PreBChkMalloc,
                                              g_minSpySize,
                                              g_maxSpySize);
                if (NULL == s_pHhyperMalloc) {
                    delete s_PostBChkMalloc;
                    goto ERR;
                }
            }
            g_pMalloc = s_pHhyperMalloc;
            break;
        case POSTBCHKMALLOC: // post runtime
            if (NULL == s_PostBChkMalloc) {
                s_PostBChkMalloc = (CMalloc*)new CPostBChkMalloc();
                if (NULL == s_PostBChkMalloc) {
                    goto ERR;
                }
                s_pHhyperMalloc = new CHyperMalloc(s_PostBChkMalloc,
                                              g_minSpySize,
                                              g_maxSpySize);
                if (NULL == s_pHhyperMalloc) {
                    delete s_PostBChkMalloc;
                    goto ERR;
                }
            }

            g_pMalloc = s_pHhyperMalloc;
            break;
        default: //malloc
            if (NULL == s_Malloc) {
                s_Malloc = new CMalloc();
                if (NULL == s_Malloc) {
                    goto ERR;
                }
            }
            g_pMalloc = s_Malloc;
    }

    if (NULL != g_pMalloc) {
        s_iState = iState;
        return TRUE;
    }

ERR:
    Aura_printf("Error in init g_pMalloc!\n");
    return FALSE;
}

#else

EXTERN_C BOOL HeapSpySwitch(int iState)
{
    CMalloc * pMalloc = NULL;
    CMalloc * pSpyMalloc = NULL;
    CMalloc * pPreBChkMalloc = NULL;
    CMalloc * pPostBChkMalloc = NULL;
    CMalloc * pHhyperMalloc = NULL;

    switch (iState) {
        case MALLOC: //malloc
            pMalloc = (CMalloc *)new CMalloc();
            if (NULL == pMalloc) {
                goto ERR;
            }
            g_pMalloc = pMalloc;
            break;
        
        case SPYMALLOC: // spy malloc            		
            pSpyMalloc = (CMSpyMalloc *) new CMSpyMalloc();
            if (NULL == pSpyMalloc) {
                goto ERR;
            }
            g_pMalloc = pSpyMalloc;
            break;
        
        case PREBCHKMALLOC: // pre runtime
            pPreBChkMalloc = (CPreBChkMalloc*) new CPreBChkMalloc();
            if (NULL == pPreBChkMalloc) {
                goto ERR;
            }
            
            pHhyperMalloc = (CHyperMalloc*) new CHyperMalloc(pPreBChkMalloc,
                                                             g_minSpySize,
                                                             g_maxSpySize,
                                                             g_valveSpySize);
            if (NULL == pHhyperMalloc) {
                delete(pPreBChkMalloc);
                goto ERR;
            }
            g_pMalloc = pHhyperMalloc;       
            break;
        
        case POSTBCHKMALLOC: // post runtime
            pPostBChkMalloc = (CPostBChkMalloc*) new CPostBChkMalloc();
            if (NULL == pPostBChkMalloc) {
                goto ERR;
            }

            pHhyperMalloc = (CHyperMalloc*) new CHyperMalloc(pPostBChkMalloc,
                                                             g_minSpySize,
                                                             g_maxSpySize,
                                                             g_valveSpySize);
            if (NULL == pHhyperMalloc) {
                delete(pPostBChkMalloc);
                goto ERR;
            }
            g_pMalloc = pHhyperMalloc; 
            break;
            
        default: //malloc
            pMalloc = (CMalloc *)new CMalloc();
            if (NULL == pMalloc) {
                goto ERR;
            }
            g_pMalloc = pMalloc;
    }
    s_iState = iState;
    return TRUE;

ERR:
    return FALSE;
}

#endif

EXTERN_C ECode ConfigGetItemStr(wchar_t*, wchar_t*);

static ECode SetSpySize()
{
    ECode ec = NOERROR;
    ec = ConfigGetItemInt(L"minimumSpySize", &g_minSpySize);
    if (FAILED(ec)) {
        return ec;
    }
    if (g_minSpySize < 0) g_minSpySize = 0;
    ec = ConfigGetItemInt(L"maximumSpySize", &g_maxSpySize);
    if (FAILED(ec)) {
        return ec;
    }
    if (g_maxSpySize == 0) {
        g_maxSpySize = 0x7fffffff;
    }
    
    ec = ConfigGetItemInt(L"valveSpySize", &g_valveSpySize);
    if (FAILED(ec)) {
        return ec;
    }
    if (g_valveSpySize < 0) {
        g_valveSpySize = 0;
    }
        
    srand (GetTickCount());

    return ec;
}

EXTERN_C BOOL SysHeapInit()
{
#ifdef _DEBUG
    wcscpy(g_traceFlag, L"on");
    if (NOERROR == ConfigGetItemStr(L"memtrace", g_traceFlag)) {
        if (0 == wcscmp(g_traceFlag, L"off")) {
            HeapSpySwitch(MALLOC);
        }
        else if (0 == wcscmp(g_traceFlag, L"preruntime")) {
            SetSpySize();
            HeapSpySwitch(PREBCHKMALLOC);
        }
        else if (0 == wcscmp(g_traceFlag, L"postruntime")) {
            SetSpySize();
            HeapSpySwitch(POSTBCHKMALLOC);
        }
        else {
            HeapSpySwitch(SPYMALLOC);
        }
    }
    else {
        HeapSpySwitch(SPYMALLOC);
    }
#else
    wcscpy(g_traceFlag, L"off");
    if (NOERROR == ConfigGetItemStr(L"memtrace", g_traceFlag)) {
        if (0 == wcscmp(g_traceFlag, L"on")) {
            HeapSpySwitch(SPYMALLOC);
        } else if (0 == wcscmp(g_traceFlag, L"preruntime")) {
            SetSpySize();
            HeapSpySwitch(PREBCHKMALLOC);
        } else if (0 == wcscmp(g_traceFlag, L"postruntime")) {
            SetSpySize();
            HeapSpySwitch(POSTBCHKMALLOC);
        } else {
            HeapSpySwitch(MALLOC);
        }
    }
    else {
        HeapSpySwitch(MALLOC);
    }
#endif
       
#ifdef _MSVC
    g_HeapDebugSum = (unsigned int)GetWindowSettingHex(L"HEAPDEBUGSUM");
    g_HeapDebugObj = (unsigned int)GetWindowSettingHex(L"HEAPDEBUGOBJ");
    if (-1 != (int)g_HeapDebugSum || -1 != g_HeapDebugObj) {
       // Aura_printf("===TRACEINFO===Debug Summ %x Obj %x\n",
       //     g_HeapDebugSum, g_HeapDebugObj);
    }
//    wchar_t *p = _wgetenv(L"HEAPDEBUGSTACK");
//    if (NULL == p) {//Get enviroment variable from carhost.cfg
//        p = _GetEnvironment(L"HEAPDEBUGSTACK");
//    }
//    if (NULL != p) {
//        g_bTrackStack = (0 == _wcsnicmp(p, L"1", 1));
    //    }
#endif

    if(NULL == g_pMalloc) {
        Aura_printf("Error in Init CMSpyMalloc!");
        return FALSE;
    }
    return TRUE;
}

EXTERN_C void * _AllocEnvironBuffer();
EXTERN_C void _FreeEnvironBuffer();

EXTERN_C BOOL __stdcall _DllMainCRTStartup(
    HANDLE hDllHandle,
    UInt32 dwReason,
    PVoid preserved)
{
    BOOL bRet = TRUE;
    switch (dwReason) {
         case DLL_PROCESS_ATTACH:
            if (!_AllocEnvironBuffer())
                return FALSE;         
            ConfigInitDic(); // Init config dictionary
            if (!ReadConfigFile()) 
                return FALSE;
            // For debugging the appointed heap memory block.
            g_HeapDebugSize = GetWindowSettingHex(L"HEAPDEBUGSIZE");
            g_HeapDebugIdx = GetWindowSettingHex(L"HEAPDEBUGINDEX");
            if (-1 != (int)g_HeapDebugSize || -1 != (int)g_HeapDebugIdx) {
                Aura_printf("===TRACEINFO===Debug Idx %d, Size %d\n",
                g_HeapDebugIdx, g_HeapDebugSize);
            }
            bRet = SysHeapInit();//Init memery heap
            break;
        
        case DLL_PROCESS_DETACH:
            CheckHeapMemory(0);
            ConfigDestroyDic();
            _FreeEnvironBuffer();
            DestroyMallocObject();
            break;
        
        default:
            return FALSE;
    }
    return bRet;
}
